package com.mali.malibackenduser.service.impl;

import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mali.backendcommon.constant.MessageConstant;
import com.mali.backendcommon.constant.RedisConstant;
import com.mali.backendcommon.exception.ShuMaException;
import com.mali.backendcommon.properties.EmailProperties;
import com.mali.backendcommon.properties.JwtProperties;
import com.mali.backendcommon.rpc.UserService;
import com.mali.backendcommon.util.EmailUtil;
import com.mali.backendcommon.util.JwtUtil;
import com.mali.backendcommon.util.RedisUtil;
import com.mali.malibackendmodel.pojo.User;
import com.mali.malibackendmodel.vo.UserLoginVO;
import com.mali.malibackendmodel.vo.UserVO;
import com.mali.malibackenduser.mapper.UserMapper;
import io.jsonwebtoken.Claims;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import java.time.LocalDateTime;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author 132993
 * @description 针对表【user(用户)】的数据库操作Service实现
 * @createDate 2024-06-17 16:46:20
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
        implements UserService{
    @Autowired
    private  EmailUtil emailUtil;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private JwtProperties jwtProperties;

    private final String loginUser="loginUser";
    @Autowired
    private EmailProperties emailProperties;


    Random random = new Random();
    @Override
    public UserLoginVO login(String account, String password) {
        // 1.校验
        if (StringUtils.isAnyBlank(account, password)) {
            throw new ShuMaException(MessageConstant.PARAMETER_IS_NULL);
        }
        //检验账户长度
        if (account.length() <4  || account.length() >20) {
            throw new ShuMaException(MessageConstant.ACCOUNT_LENGTH_ERROR);
        }
        //检验密码长度
        if (password.length() < 8||password.length() > 16) {
            throw new ShuMaException(MessageConstant.PASSWORD_LENGTH_ERROR);
        }
        //2.缓存获取数据
        User user= (User) redisUtil.get(RedisConstant.userAccount+account);
        if(user==null)
        {
            //缓存没有 根据账号查询数据库中的数据
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("account", account);
            user = this.baseMapper.selectOne(queryWrapper);

        }
        //3.添加缓存
        redisUtil.set(RedisConstant.userAccount+account,user);
        //4.处理各种异常情况（用户名不存在、密码不对）
        if (user == null) {
            //账号不存在
            throw new ShuMaException(MessageConstant.ACCOUNT_NOT_FOUND);
        }
        //进行md5加密，然后再进行比对
        String encryptPassword=DigestUtils.md5DigestAsHex(password.getBytes());
        if (!encryptPassword.equals(user.getPassword())) {
            //密码错误
            throw new ShuMaException(MessageConstant.PASSWORD_ERROR);
        }
        //5.登录成功 生成jwt令牌
        Map<String, Object> claims = new HashMap<>();
        //密码置空
        user.setPassword(null);
        String loginUserString;
        try {
            loginUserString = JSONUtil.toJsonStr(user);
        } catch (Exception e) {
            throw new RuntimeException(MessageConstant.JSON_CONVERSION_ERROR);
        }
        //存放用户信息
        claims.put(loginUser,loginUserString );
        //存放唯一jti 用于验证退出
        String jti = UUID.randomUUID().toString();
        claims.put("jti", jti);
        String token = JwtUtil.createJWT(
                jwtProperties.getUserSecretKey(),
                jwtProperties.getUserTtl(),
                claims);
        //6.返回数据
        UserLoginVO userLoginVO = new UserLoginVO();
        BeanUtils.copyProperties(user,userLoginVO);
        userLoginVO.setToken(token);
        return userLoginVO;
    }

    @Override
    public void sendCode(String account,String email) {
        //1.判断参数是否为空
        if (StringUtils.isAnyBlank(account, email)) {
            throw new ShuMaException(MessageConstant.PARAMETER_IS_NULL);
        }
        //检验账户长度
        if (account.length() <4  || account.length() >20) {
            throw new ShuMaException(MessageConstant.ACCOUNT_LENGTH_ERROR);
        }
        //检验邮箱格式
        boolean isEmailValidFlag = isEmailValid(email);
        if(!isEmailValidFlag)
        {
            throw new ShuMaException(MessageConstant.EMAIL_FORMAT_ERROR);
        }

        //2.设置分布式锁，时间1分钟，避免频繁请求
        boolean isLockSet=redisUtil.setnx(RedisConstant.userLock +account,null,60);
        if(!isLockSet)
        {
            throw new ShuMaException(MessageConstant.Code_REQUEST_TOO_FREQUENT);

        }
        //3.发送邮件
        String code = String.valueOf(random.nextInt(900000) + 100000);
        //邮件标题
        String subject = emailProperties.getSubject();
        //正文内容
        String content = emailProperties.getContentPreCode()+code+"，"+emailProperties.getContentAfterCode();
        sendEmail(email,subject,content);
        String encryptPassword = DigestUtils.md5DigestAsHex(code.getBytes());
        redisUtil.set(RedisConstant.userCode +email,encryptPassword,180);

    }
    private void sendEmail(String email, String subject, String content) {
        emailUtil.sendEmail(email, subject, content);
    }


    public static boolean isEmailValid(String email) {
        String emailRegex = "^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^-]+(?:\\.[a-zA-Z0-9_!#$%&'*+/=?`{|}~^-]+)*@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*$";
        Pattern pattern = Pattern.compile(emailRegex);
        Matcher matcher = pattern.matcher(email);
        return matcher.matches();
    }

    @Override
    public void register(String account, String userName, String password, String email, String securityCode) {
        //1.参数非空校验
        if (StringUtils.isAnyBlank(account,userName, password,email,securityCode)) {
            throw new ShuMaException(MessageConstant.PARAMETER_IS_NULL);
        }
        //检验账户长度
        if (account.length() <4  || account.length() >20) {
            throw new ShuMaException(MessageConstant.ACCOUNT_LENGTH_ERROR);
        }
        //检验用户名长度
        if (userName.length() < 4 || userName.length() >16) {
            throw new ShuMaException(MessageConstant.USERNAME_LENGTH_ERROR);
        }
        //检验密码长度
        if (password.length() < 8||password.length() > 16) {
            throw new ShuMaException(MessageConstant.PASSWORD_LENGTH_ERROR);
        }
        //检验邮箱长度
        if (email.length() > 64) {
            throw new ShuMaException(MessageConstant.EMAIL_LENGTH_ERROR);
        }
        //检验验证码长度
        if(securityCode.length()!=6)
        {
            throw new ShuMaException(MessageConstant.Code_LENGTH_ERROR);
        }
        //检验密码格式
        if (!isValidPassword(password))
        {
            throw new ShuMaException(MessageConstant.PASSWORD_FORMAT_ERROR);
        }
        //检验邮箱格式
        if (!isEmailValid(email))
        {
            throw new ShuMaException(MessageConstant.EMAIL_FORMAT_ERROR);
        }
        //3.检验验证码是否正确
        String code = (String) redisUtil.get(RedisConstant.userCode+ email);
        if( !DigestUtils.md5DigestAsHex(securityCode.getBytes()).equals(code)) {
            throw new ShuMaException(MessageConstant.Code_ERROR);
        }
        //3.缓存获取用户数据
        User user= (User) redisUtil.get(RedisConstant.userAccount+account);
        if(user==null)
        {
            //缓存没有 根据账号查询数据库中的数据
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("account", account);
            user = this.baseMapper.selectOne(queryWrapper);

        }
        //4.限制账号唯一
        if(user!=null)
        {
            throw new ShuMaException(MessageConstant.USER_IS_EXIST);
        }
        //5.保存用户
        // 密码加密
        String encryptPassword=DigestUtils.md5DigestAsHex(password.getBytes());
        User addUser=new User();
        addUser.setAccount(account);
        addUser.setPassword(encryptPassword);
        addUser.setUserName(userName);
        addUser.setRole(1);
        addUser.setEmail(email);
        addUser.setCreateTime(LocalDateTime.now());
        addUser.setUpdateTime(LocalDateTime.now());
        this.save(addUser);
        //6.添加缓存
        redisUtil.set(RedisConstant.userAccount+account,user);

    }

    @Override
    public void resetPassword(String account, String securityCode, String newPassword) {
        //1.参数检验
        if (StringUtils.isAnyBlank(account, securityCode,newPassword)) {
            throw new ShuMaException(MessageConstant.PARAMETER_IS_NULL);
        }
        //检验账户长度
        if (account.length() <4  || account.length() >20) {
            throw new ShuMaException(MessageConstant.ACCOUNT_LENGTH_ERROR);
        }
        //检验验证码长度
        if(securityCode.length()!=6)
        {
            throw new ShuMaException(MessageConstant.Code_LENGTH_ERROR);
        }
        //检验密码格式
        if (!isValidPassword(newPassword))
        {
            throw new ShuMaException(MessageConstant.PASSWORD_FORMAT_ERROR);
        }
        //2.缓存获取数据
        User user= (User) redisUtil.get(RedisConstant.userAccount+account);
        if(user==null)
        {
            //缓存没有 根据账号查询数据库中的数据
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("account", account);
            user = this.baseMapper.selectOne(queryWrapper);

        }
        if (user == null) {
            //账号不存在
            throw new ShuMaException(MessageConstant.ACCOUNT_NOT_FOUND);
        }
        //3.获取邮箱 检验验证码
        String userEmail = user.getEmail();
        String code = (String) redisUtil.get(RedisConstant.userCode+ userEmail);
        if( !DigestUtils.md5DigestAsHex(securityCode.getBytes()).equals(code)) {
            throw new RuntimeException(MessageConstant.Code_ERROR);
        }
        //4.重置密码
        String encryptPassword=DigestUtils.md5DigestAsHex(newPassword.getBytes());
        user.setPassword(encryptPassword);
        //5.删除缓存
        redisUtil.del(RedisConstant.userId+user.getId(),RedisConstant.userAccount+user.getAccount());
        //6.更新密码
        this.updateById(user);

    }

    @Override
    public List<UserVO> getListByUserName(String userName) {
        if (StringUtils.isEmpty(userName) ) {
            throw new ShuMaException(MessageConstant.PARAMETER_IS_NULL);
        }
        List<UserVO> userVOList = userMapper.selectListByUsername(userName);
        return userVOList;
    }

    @Override
    public UserVO getByUserId(Long id) {
        //参数非空判断
        if (Objects.isNull(id) || id < 0) {
            throw new ShuMaException(MessageConstant.PARAMETER_IS_NULL);
        }
        User user=this.getById(id);
        // 用户不存在
        if (Objects.isNull(user)) {
            throw new ShuMaException(MessageConstant.USER_NOT_EXIST);
        }
        //copy对象返回
        UserVO userVO=new UserVO();
        BeanUtils.copyProperties(user,userVO);
        //将缓存存入redis
        redisUtil.set(RedisConstant.userId+user.getId(),user);
        return userVO;
    }
    @Override
    public List getByUserIds(List ids) {
        //参数非空判断
        if (Objects.isNull(ids) || ids.isEmpty()) {
            throw new ShuMaException(MessageConstant.PARAMETER_IS_NULL);
        }
        List users = this.listByIds(ids);
        return users;
    }

    @Override
    public void logout(String token) {
        try {
            //解析JWT
            Claims claims = JwtUtil.parseJWT(jwtProperties.getUserSecretKey(), token);
            String jti = claims.get("jti", String.class);
            System.out.println(jti);
            if (StringUtils.isNotBlank(jti)) {
                // 添加至黑名单
                redisUtil.set(jti,"1",jwtProperties.getUserTtl()/1000);
            } else {
                throw new RuntimeException(MessageConstant.TOKEN_IS_NULL);
            }
        } catch (Exception e) {
            throw new RuntimeException(MessageConstant.JWT_PARSER_ERROR);

        }
    }


    public static boolean isValidPassword(String password) {
        if (password == null ) {
            return false;
        }

        String pattern = "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z]).*$";
        return password.matches(pattern);
    }


}




